package eiisan.config.aop.request.advisor;

import com.google.common.util.concurrent.RateLimiter;
import eiisan.config.aop.request.annotation.Limit;
import eiisan.config.aop.util.AOPUtil;
import eiisan.constant.errors.exception.CodeCheckException;
import eiisan.constant.settings.DefTime;
import eiisan.util.model.pair.Pair;
import eiisan.util.model.response.Result;
import eiisan.util.reflect.ReflectUtil;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求限流AOP
 * RateLimiter https://www.jianshu.com/p/5d4fe4b2a726
 */
@Component
public class LimitUserAdvisor implements PointcutAdvisor {

	/**
	 * 限流器
	 */
	private Map<String, Pair<RateLimiter, Long>> limitCache = new HashMap<>();

	/**
	 * AOP切点
	 */
	@Override
	public Advice getAdvice() {
		/**
		 * 请求限流
		 * @param JoinPoint AOP切点
		 */
		return (MethodInterceptor) invocation -> {
			Pair<RateLimiter, Long> pair = limitCache.get(ReflectUtil.getFullMethodName(invocation.getMethod()));
			if (!pair.getKey().tryAcquire(pair.getValue(), DefTime.UNIT)) {
				return Result.busyServe();
			}
			return invocation.proceed();
		};
	}

	/**
	 * 默认true
	 */
	@Override
	public boolean isPerInstance() {
		return true;
	}

	/**
	 * 切入点配置
	 */
	@Override
	public Pointcut getPointcut() {
		return new Pointcut() {

			/**
			 * 根据方法判断
			 */
			@Override
			public MethodMatcher getMethodMatcher() {
				return new MethodMatcher() {

					/**
					 * 对方法进行判断(运行时)
					 */
					@Override
					public boolean matches(Method method, Class<?> targetClass, Object... args) {
						//isRuntime()方法返回值为false时,不会进行运行时判断
						return false;
					}

					/**
					 * 对方法进行判断
					 */
					@Override
					public boolean matches(Method method, Class<?> targetClass) {
						//这里可以进行运行前检查
						Limit annotation = method.getAnnotation(Limit.class);
						if (annotation == null) {
							return false;
						}
						if (annotation.value() <= 0) {
							throw new CodeCheckException("方法", ReflectUtil.getFullMethodName(method),
								"的每秒每token限制请求数必须大于0!");
						}
						if (annotation.timeout() <= 0) {
							throw new CodeCheckException("方法", ReflectUtil.getFullMethodName(method), "的超时时间必须大于0!");
						}
						if (!AOPUtil.isResultAsReturn(method)) {
							return false;
						}
						Pair<RateLimiter, Long> pair = new Pair<>(RateLimiter.create(annotation.value()), annotation
							.timeout());
						limitCache.put(ReflectUtil.getFullMethodName(method), pair);
						return true;
					}

					/**
					 * 默认true
					 */
					@Override
					public boolean isRuntime() {
						return false;
					}
				};
			}

			/**
			 * 根据类型判断
			 */
			@Override
			public ClassFilter getClassFilter() {
				return clazz -> true;
			}

		};
	}

}
